/*
* gpio_irq_test.c- Sigmastar
*
* Copyright (c) [2019~2020] SigmaStar Technology.
*
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License version 2 for more details.
*
*/

#include <linux/types.h> 
#include <linux/kernel.h> 
#include <linux/delay.h> 
#include <linux/ide.h> 
#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/errno.h>
#include <linux/gpio.h> 
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/moduleparam.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include "cam_os_wrapper.h"

#define REQUEST_CNT 1 /* 设备号个数 */
#define IRQ_NAME "gpio90IrqDev" /* 名字 */
#define TRUE 0X01
#define FALSE 0X00


s32 gpio_set_success = 0;
u32 gpio_irq_num = 0;
int pin = 90;// gpio90
int level = 0;


/* irqDev 设备结构体 */
struct irq_dev{
	dev_t devid; /* 设备号 */
	struct cdev cdev; /* cdev */
	struct class *class; /* 类 */
	struct device *device; /* 设备 */
	int major; /* 主设备号 */
	int minor; /* 次设备号 */
	struct device_node *nd; /* 设备节点 */
	atomic_t haveIrqFlag; /* 标记是否检测到中断信号*/
};

struct irq_dev irqDev; /* irq 设备 */


irqreturn_t gpio90_handler(int irq, void *dev_instance)
{
    CamOsTimespec_t ptRes;
    CamOsGetMonotonicTime(&ptRes);
    printk("%s  [%d.%09d]\n", __func__, ptRes.nSec, ptRes.nNanoSec);
	atomic_set(&irqDev.haveIrqFlag, TRUE);
    return IRQ_NONE;
}

/*
 * @description : 中断 IO 初始化
 * @param : 无
 * @return : 无
 */
static int irqio_init(void)
{
    gpio_set_success = 0;

    if(gpio_request(pin, "gpio_irq_test") < 0)
    {
        printk("request gpio[%d] failed...\n", pin);
        return 0;
    }

    if (gpio_direction_input(pin) < 0) {
        printk("gpio_direction_input[%d] failed...\n", pin);
        return 0;
    }

    gpio_irq_num = gpio_to_irq(pin);
    if (request_irq(gpio_irq_num, gpio90_handler, (level==0)? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING, "PAD", NULL))
    {
        printk(KERN_ERR"can't allocate irq\n");
        return 0;
    }

    gpio_set_success = 1;
    printk("request gpio irq success, gpio_set_success=%d\n", gpio_set_success);
    return 0;
}

/*
 * @description : 打开设备
 * @param – inode : 传递给驱动的 inode
 * @param - filp : 设备文件，file 结构体有个叫做 private_data 的成员变量
 * 一般在 open 的时候将 private_data 指向设备结构体。
 * @return : 0 成功;其他 失败
 */
static int imx6uirq_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &irqDev; /* 设置私有数据 */
	return 0;
}

/*
 * @description : 从设备读取数据
 * @param – filp : 要打开的设备文件(文件描述符)
 * @param – buf : 返回给用户空间的数据缓冲区
 * @param - cnt : 要读取的数据长度
 * @param – offt : 相对于文件首地址的偏移
 * @return : 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t imx6uirq_read(struct file *filp, char __user *buf,
		size_t cnt, loff_t *offt)
{
	int ret = 0;
	unsigned char irqFlag = 0;
	struct irq_dev *dev = (struct irq_dev *)filp->private_data;

	irqFlag = atomic_read(&dev->haveIrqFlag);

	if (irqFlag) { /* 有检测到中断 */
		ret = copy_to_user(buf, &irqFlag, sizeof(irqFlag));
		atomic_set(&dev->haveIrqFlag, FALSE); /* 中断标志清零 */
	} else {
		goto data_error;
	}
	return 0;

data_error:
	return -EINVAL;
}

/* 设备操作函数 */
static struct file_operations irqFileOperations = {
	.owner = THIS_MODULE,
	.open = imx6uirq_open,
	.read = imx6uirq_read,
};

/*
 * @description : 驱动入口函数
 * @param : 无
 * @return : 无
 */
static int __init gpio_irq_init(void)
{
	/* 1、构建设备号 */
	//注册一个主设备号，由内核自动分配
	alloc_chrdev_region(&irqDev.devid, 0, REQUEST_CNT,IRQ_NAME);
	//宏 MAJOR 从设备号 devid 中提取主设备号。宏 MINOR 用来从设备号 devid 中提取次设备号。
	irqDev.major = MAJOR(irqDev.devid);
	irqDev.minor = MINOR(irqDev.devid);


	/* 2、注册字符设备 */
	cdev_init(&irqDev.cdev, &irqFileOperations);
	cdev_add(&irqDev.cdev, irqDev.devid, REQUEST_CNT);

	/* 3、创建类 */
	irqDev.class = class_create(THIS_MODULE, IRQ_NAME);
	if (IS_ERR(irqDev.class)) {
		return PTR_ERR(irqDev.class);
	}

	/* 4、创建设备 */
	irqDev.device = device_create(irqDev.class, NULL,
			irqDev.devid, NULL, IRQ_NAME);
	if (IS_ERR(irqDev.device)) {
		return PTR_ERR(irqDev.device);
	}

	/* 5、初始化中断标志 */
	atomic_set(&irqDev.haveIrqFlag, FALSE);
	irqio_init();
	return 0;
}

/*
 * @description : 驱动出口函数
 * @param : 无
 * @return : 无
 */
static void __exit gpio_irq_exit(void)
{
    if (gpio_set_success)
        free_irq(gpio_irq_num, NULL);
    gpio_free(pin);

	cdev_del(&irqDev.cdev);
	unregister_chrdev_region(irqDev.devid, REQUEST_CNT);
	device_destroy(irqDev.class, irqDev.devid);
	class_destroy(irqDev.class);
}

module_init(gpio_irq_init);
module_exit(gpio_irq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ssd20x");

